package com.ease.util.gc {
import com.ease.util.configuration.ConfigurationUtil;

import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;

/**
 * A memory profiler friendly a property provider that takes advantage of global
 * instances.
 *
 * This property provider will avoid storing the global instance which is provided for
 * by the globalGetter.  This simplifies the object graph for the memory profiler.
 */
public class SharedReference {
   private static const GLOBAL_VALUE:Object = {};

   //----------------------------------------------------------------
   // Static Properties

   //------------------------------------------------------
   // breakGCGraph
   private static var _breakGCGraph:Boolean = false;

   /**
    * Property that control if memory profiler gc root graph simplification is enabled.
    */
   public static function get breakGCGraph():Boolean {
      return _breakGCGraph;
   }

   /**
    * @private
    */
   public static function set breakGCGraph(value:Boolean):void {
      _breakGCGraph = value;
   }

   //----------------------------------------------------------------
   // Member variables

   private var _globalGetter:Function;
   private var _object:* = null;
   private var _className:String = null;
   private var _propName:String = null;

   /**
    * Constructs a new <code>SharedReference</code> instance.
    */
   public function SharedReference(token:Class) {
      if (token != PrivateConstructorToken) {
         throw new Error("SharedReference cannot be instantiated directly.");
      }
   }

   /**
    * Creates a new <code>SharedReference</code> instance.
    *
    * @param globalGetter
    *    The global instance getter.
    */
   public static function newReferenceByGetter(globalGetter:Function):SharedReference {
      var ref:SharedReference = new SharedReference(PrivateConstructorToken);
      ref._globalGetter = globalGetter;
      return ref;
   }

   /**
    * Creates a new <code>SharedReference</code> instance.
    *
    * @param clazz
    *    The class defining the instance property.
    *
    * @param propName
    *    The name of the global instance property.
    */
   public static function newReference(clazz:Class, propName:String = "instance")
      : SharedReference {
      var ref:SharedReference = new SharedReference(PrivateConstructorToken);
      if (SharedReference.breakGCGraph) {
         ref._className = getQualifiedClassName(clazz);
         ref._propName = propName;
         ref._globalGetter = ref.clazzPropGetter;
      }
      return ref;
   }

   /**
    * The object to which this <code>SharedReference</code> refers.
    */
   public function get object():* {
      if (_object == GLOBAL_VALUE) {
         return _globalGetter();
      }
      return _object;
   }

   /**
    * @private
    */
   public function set object(value:*):void {
      if (SharedReference.breakGCGraph && _globalGetter() == value) {
         _object = GLOBAL_VALUE;
      } else {
         _object = value;
      }
   }

   private function clazzPropGetter():* {
      // ref: http://nondocs.blogspot.com/2007/04/flexhowtoinstantiate-class-from-class.html
      var clazz:Class = getDefinitionByName(_className) as Class;
      return clazz[_propName];
   }
}
}

// http://livedocs.adobe.com/flex/3/html/03_Language_and_Syntax_05.html#151506
class PrivateConstructorToken {}
